package com.net.xpay.notify.service;

import com.alipay.api.msg.MsgHandler;
import com.google.gson.JsonObject;
import com.net.common.exception.BusinessException;
import com.net.xpay.alipay.safePay.PoseidonSafePayAlipayMsgClient;
import com.net.xpay.common.constant.GenIdPrefixConstant;
import com.net.xpay.common.domain.UserShard;
import com.net.xpay.common.helper.NoticeHelper;
import com.net.xpay.common.manager.UserShardManager;
import com.net.xpay.common.service.FlexibleStaffAuthenticationService;
import com.net.xpay.common.service.SafePayBookPreApplyService;
import com.net.xpay.core.domain.safePay.BookAccountChargeOrder;
import com.net.xpay.core.enums.SafePayOrderStatus;
import com.net.xpay.core.model.BookAccountChargeOrderNotifyModel;
import com.net.xpay.core.service.safePay.FlexibleStaffCardService;
import com.net.xpay.core.service.safePay.SafePayService;
import com.net.xpay.core.utils.GsonUtil;
import com.net.shard.ShardDataSource;
import com.net.shard.ShardDataSourceConfigurer;
import com.net.shard.ShardUtil;
import com.net.shard.domain.ShardConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import javax.sql.DataSource;
import java.time.LocalDateTime;
import java.util.Map;

/**
 * 安全付消息监听
 */
@Slf4j
@Service
public class SafePayAlipayMsgService extends Thread {

    @Autowired
    private PoseidonSafePayAlipayMsgClient xpaySafePayAlipayMsgClient;

    @Autowired
    private SafePayService safePayService;

    @Autowired
    private FlexibleStaffCardService flexibleStaffCardService;

    @Autowired
    private NoticeHelper noticeHelper;

    @Autowired
    private UserShardManager userShardManager;

    @Autowired
    @Qualifier("shardDataSource")
    private DataSource shardDataSource;

    @Autowired
    private FlexibleStaffAuthenticationService flexibleStaffAuthenticationService;

    @Autowired
    private SafePayBookPreApplyService safePayBookPreApplyService;


    @Override
    public void run() {
        xpaySafePayAlipayMsgClient.getAlipayMsgClient().setMessageHandler(new MsgHandler() {
            /**
             * 客户端接收到消息后回调此方法
             *  @param  msgApi 接收到的消息的消息api名
             *  @param  msgId 接收到的消息的消息id
             *  @param  bizContent 接收到的消息的内容，json格式
             */
            public void onMessage(String msgApi, String msgId, String bizContent) {
                log.info("safePay receive message. msgApi:{}|msgId:{}|bizContent:{}", msgApi, msgId, bizContent);
                if ("alipay.fund.trans.order.changed".equals(msgApi)) {
                    processOrderChangeMsg(bizContent);
                } else if ("alipay.fund.trans.refund.success".equals(msgApi)) {
                    processOrderRefundMsg(bizContent);
                } else if ("alipay.fund.accountbook.trans.notify".equals(msgApi)) {
                    processAccountBookTransMsg(bizContent);
                } else if ("alipay.fund.accountbook.notify".equals(msgApi)) {
                    //记账本开通通知
                    processAccountBookMsg(bizContent);
                } else if ("alipay.fund.flexiblestaffing.employeehome.notify".equals(msgApi)) {
                    //零工卡开通通知
                    processOpenFlexibleStaffCardMsg(bizContent);
                }
            }
        });
        try {
            xpaySafePayAlipayMsgClient.getAlipayMsgClient().connect();
        } catch (InterruptedException e) {
            log.error("alipayMsgService connect error", e);
            return;
        }
    }

    private void processAccountBookMsg(String bizContent) {
        log.warn("processAccountBookMsg bizContent={}", bizContent);
        JsonObject bizObj = GsonUtil.parse(bizContent).getAsJsonObject();

        String outBizNo = bizObj.get("out_biz_no").getAsString();
        JsonObject authInfo = GsonUtil.parse(bizObj.get("auth_info").getAsString()).getAsJsonObject();
        String agreementNo = authInfo.get("agreement_no").getAsString();
        String extCardInfo = bizObj.get("ext_card_info").getAsString();
        String accountBookId = bizObj.get("account_book_id").getAsString();

        if (outBizNo.startsWith(GenIdPrefixConstant.BP)) {
            //预创建的 此时商户还未创建
            log.info("onBookCreated start bizContent={}", bizContent);
            safePayBookPreApplyService.onBookCreated(outBizNo, agreementNo, extCardInfo, accountBookId);
            log.info("onBookCreated end bizContent={}", bizContent);
            return;
        }

        try {
            Long userId = Long.parseLong(outBizNo.split("-")[0]);
            UserShard userShard = userShardManager.getById(userId);
            ShardUtil.setShardContext(userShard.getShardId());
            safePayService.updateSafePayAccount(outBizNo, agreementNo, extCardInfo, accountBookId);
        } finally {
            ShardUtil.cleanShardContext();
        }

    }


    private void processOpenFlexibleStaffCardMsg(String bizContent) {
        log.warn("processFlexibleStaffCardMsg bizContent={}", bizContent);
        JsonObject bizObj = GsonUtil.parse(bizContent).getAsJsonObject();
        String outBizNo = bizObj.get("out_biz_no").getAsString();
        if (outBizNo.startsWith("P")) {
            //视频核验渠道开通的 没有记账本信息
            JsonObject agreementInfo = GsonUtil.parse(bizObj.get("rent_agreement_info").getAsString()).getAsJsonObject();
            String alipayLogonId = agreementInfo.get("rent_logon_id").getAsString();
            String alipayUid  = agreementInfo.get("rent_user_id").getAsString();
            String employeeCardNo = bizObj.get("employee_card_no").getAsString();
            flexibleStaffAuthenticationService.updateFlexibleStaffAuthenticationCardInfo(outBizNo, employeeCardNo, alipayLogonId, alipayUid);
        } else {
            JsonObject agreementInfo = GsonUtil.parse(bizObj.get("rent_agreement_info").getAsString()).getAsJsonObject();
            String agreementNo = agreementInfo.get("agreement_no") == null ? null : agreementInfo.get("agreement_no").getAsString();
            String alipayUid  = agreementInfo.get("rent_user_id").getAsString();

            String employeeCardNo = bizObj.get("employee_card_no").getAsString();

            JsonObject serviceInfo = GsonUtil.parse(bizObj.get("rent_service_info").getAsString()).getAsJsonObject();
            String bookId = serviceInfo.get("account_book_id") == null ? null : serviceInfo.get("account_book_id").getAsString();
            flexibleStaffCardService.updateFlexibleStaffCard(outBizNo, agreementNo, alipayUid, bookId, employeeCardNo);
        }
    }



    private void processAccountBookTransMsg(String bizContent) {
        log.warn("processAccountBookTransMsg bizContent={}", bizContent);
        JsonObject bizObj = GsonUtil.parse(bizContent).getAsJsonObject();
        String notify_scene = bizObj.get("notify_scene").getAsString();
        if (!"IN".equals(notify_scene)) {
            //只记录入金
            return;
        }
        String payeeIdentityType = bizObj.get("payee_identity_type").getAsString();
        if (!"ACCOUNT_BOOK_ID".equals(payeeIdentityType)) {
            log.warn("processAccountBookTransMsg payeeIdentityType no right|bizContent={}", bizContent);
            noticeHelper.sendSystemMonitorNotice("【警告】安全付同名银行卡入金类型异常 alipay.fund.accountbook.trans.notify", "text");
            return;
        }
        String source = bizObj.get("source").getAsString();
        String bookId = bizObj.get("payee_identity").getAsString();
        String amount = bizObj.get("amount").getAsString();
        String orderNo = bizObj.get("order_no").getAsString();

        String payerIdentity = bizObj.get("payer_identity").getAsString();
        String payerIdentityType = bizObj.get("payer_identity_type").getAsString();
        String payerName = bizObj.get("payer_name").getAsString();

        BookAccountChargeOrderNotifyModel model = new BookAccountChargeOrderNotifyModel();
        model.setTransAmount(amount);
        model.setBookId(bookId);
        model.setSource(source);
        model.setOrderNo(orderNo);
        model.setPayerIdentity(payerIdentity);
        model.setPayerIdentityType(payerIdentityType);
        model.setPayerName(payerName);

        ShardDataSourceConfigurer shardDataSourceConfigurer = ((ShardDataSource) shardDataSource).getShardDataSourceConfigurer();
        Map<Long, ShardConfig> shardConfigMap = shardDataSourceConfigurer.getShardConfigMap();
        for (Long shardId : shardConfigMap.keySet()) {
            try {
                ShardUtil.setShardContext(shardId);
                BookAccountChargeOrder order = safePayService.notifyCreateBookAccountChargeOrder(model);
                if (order != null) {
                    return;
                }
            } catch (Exception e) {
                log.warn("processAccountBookTransMsg error", e);
                noticeHelper.sendSystemMonitorNotice("【警告】安全付同名银行卡入金异常", "text");
                throw e;
            } finally {
                ShardUtil.cleanShardContext();
            }
        }
    }

    private void processOrderRefundMsg(String bizContent) {
        log.warn("processOrderRefundMsg bizContent={}", bizContent);
        noticeHelper.sendSystemMonitorNotice("【警告】安全付单笔推送 alipay.fund.trans.refund.success processOrderRefundMsg", "text");
    }


    private void processOrderChangeMsg(String bizContent) {
        //支付订单状态发生改变
        JsonObject bizObj = GsonUtil.parse(bizContent).getAsJsonObject();

        String bizScene = bizObj.get("biz_scene").getAsString();
        String productCode = bizObj.get("product_code").getAsString();
        if ("ENTRUST_ALLOCATION".equals(bizScene) && "SINGLE_TRANSFER_NO_PWD".equals(productCode)) {
            //记账本资金调拨
            String status = bizObj.get("status").getAsString();
            String outBizNo = bizObj.get("out_biz_no").getAsString();
            String payFundOrderId = bizObj.get("pay_fund_order_id").getAsString();
            String orderId = bizObj.get("order_id").getAsString();
            if (!"SUCCESS".equals(status)) {
                return;
            }

            safePayService.notifyTransferBookAccountSuccess(outBizNo, payFundOrderId, orderId);
            return;
        }

        if ("ENTRUST_REPAYMENT".equals(bizScene) && "FLEXIBLE_STAFFING_TRANSFER".equals(productCode)) {
            //灵工卡支出
            String orderId = bizObj.get("order_id").getAsString();
            String outBizNo = bizObj.get("out_biz_no").getAsString();
            String payFundOrderId = bizObj.get("pay_fund_order_id").getAsString();
            onRepaymentTransferNotify(outBizNo, orderId, payFundOrderId);
            return;
        }

        if (!"ENTRUST_TRANSFER".equals(bizScene) || !"SINGLE_TRANSFER_NO_PWD".equals(productCode)) {
            if (!"SATF_DEPOSIT".equals(bizScene)) {
                log.warn("processOrderChangeMsg bizScene or productCode no right|bizContent={}", bizContent);
                noticeHelper.sendSystemInfoNotice("【通知】安全付单笔未知推送业务场景！|bizScene=" + bizScene, "text");
            }
            return;
        }

        String status = bizObj.get("status").getAsString();

        if (SafePayOrderStatus.DEALING.getValue().equals(status)) {
            //还在处理中
            return;
        }
        if (SafePayOrderStatus.SUCCESS.getValue().equals(status)) {
            //成功了
            String outBizNo = bizObj.get("out_biz_no").getAsString();
            processOrder(bizContent, outBizNo, SafePayOrderStatus.SUCCESS, null, null);
            return;
        }

        if (SafePayOrderStatus.FAIL.getValue().equals(status)) {
            //失败了
            String outBizNo = bizObj.get("out_biz_no").getAsString();
            String errorMsg = bizObj.get("fail_reason").getAsString();
            String errorCode = bizObj.get("error_code").getAsString();
            processOrder(bizContent, outBizNo, SafePayOrderStatus.FAIL, errorMsg, errorCode);
            return;
        }

        if (SafePayOrderStatus.REFUND.getValue().equals(status)) {
            //退票了
            noticeHelper.sendSystemMonitorNotice("【退票警告】安全付单笔推送支付结果有退票订单", "text");
            String outBizNo = bizObj.get("out_biz_no").getAsString();
            processOrder(bizContent, outBizNo, SafePayOrderStatus.REFUND, null, null);
            return;
        }

        log.warn("processOrderChangeMsg failed|bizContent={}", bizContent);
        noticeHelper.sendSystemMonitorNotice("【警告】安全付单笔推送支付结果处理失败,未知的订单状态", "text");

        throw new RuntimeException("failed");
    }

    private void onRepaymentTransferNotify(String outBizNo, String orderId, String payFundOrderId) {
        //格式  env-10018-1111111111
        if (!outBizNo.contains("-")) {
            return;
        }
        String[] contents = outBizNo.split("-");
        if (contents.length != 3) {
            return;
        }
        long userId;
        long id;
        try {
            userId = Long.parseLong(contents[1]);
        } catch (Exception e) {
            log.info("onRepaymentTransferNotify parse contents failed|outBizNo={}", outBizNo, e);
            return;
        }

        UserShard userShard = userShardManager.getById(userId);
        //异常失败重试10秒
        LocalDateTime deadline = LocalDateTime.now().plusSeconds(10L);
        while (LocalDateTime.now().isBefore(deadline)) {
            try {
                ShardUtil.setShardContext(userShard.getShardId());
                flexibleStaffCardService.onRepaymentTransferNotify(outBizNo, orderId, payFundOrderId);
            } catch (Exception e) {
                log.warn("onRepaymentTransferNotify failed|outBizNo={}", outBizNo, e);
                if (!(e instanceof BusinessException)) {
                    continue;
                }
            } finally {
                ShardUtil.cleanShardContext();
            }
            return;
        }
        log.warn("onRepaymentTransferNotify final failed|outBizNo={}", outBizNo);

        throw new RuntimeException("failed");

    }

    private void processOrder(String bizContent, String outBizNo, SafePayOrderStatus status, String errorMsg, String errorCode) {
        //批量转账批次号标准格式  env-10018-1111111111
        if (!outBizNo.contains("-")) {
            return;
        }
        String[] contents = outBizNo.split("-");
        if (contents.length != 3) {
            return;
        }
        long userId;
        long id;
        try {
            userId = Long.parseLong(contents[1]);
            id = Long.parseLong(contents[2]);
        } catch (Exception e) {
            log.info("parse contents failed|bizContent={}", bizContent, e);
            return;
        }

        UserShard userShard = userShardManager.getById(userId);
        //异常失败重试10秒
        LocalDateTime deadline = LocalDateTime.now().plusSeconds(10L);
        while (LocalDateTime.now().isBefore(deadline)) {
            try {
                ShardUtil.setShardContext(userShard.getShardId());
                safePayService.updateSafePayOrder(id, status, errorMsg, errorCode);
            } catch (Exception e) {
                log.warn("processOrderSuccess failed|bizContent={}", bizContent, e);
                if (!(e instanceof BusinessException)) {
                    continue;
                }
            } finally {
                ShardUtil.cleanShardContext();
            }
            return;
        }
        log.warn("processOrder failed|bizContent={}", bizContent);
        noticeHelper.sendSystemMonitorNotice("【警告】安全付单笔推送支付结果处理失败|outBizNo" + outBizNo, "text");

        throw new RuntimeException("failed");
    }
}
